package com.tofacebook.spring.jdbc.service.impl;

import com.tofacebook.spring.jdbc.mapper.BookMapper;
import com.tofacebook.spring.jdbc.service.BookService;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Isolation;
import org.springframework.transaction.annotation.Propagation;
import org.springframework.transaction.annotation.Transactional;

import java.util.concurrent.TimeUnit;

/**
 * 服务层，需要使用注解@Service 将其注入到类中，并且交给ioc管理
 */
@Service

class BookServiceImpl implements BookService {
    private static  final Logger log = LoggerFactory.getLogger(BookServiceImpl.class);
    //服务层需要访问持久层，是不是要将持久层注入进来
    @Autowired
    private BookMapper bookMapper;

    /**
     * 下面的方法中 没有事务，那么就会导致一种情况：
     * 没有一个执行sql的过程都独占一个事务，各自使用自己事务去提交数据，并改变数据库中表中的数据结果
     * 那么就好导致数据错乱，我们需要在方法上加上一个事务，不管该方法中有多少个sql要被执行，
     * 都是只有事务去提交，说白了：就是要么都成功，要么都失败，不允许及个别数据别改变
     * @param bookId
     * @param userId
     * @return
     *
     * 如何开始使用事务
     * 1.在spring config配置文件构造我们事务管理器：DataSourceTransactionManager
     * 2.已注解驱动的方式开始事务的管理
     * 3.需要在对应的实现类方法上加上我们注解，那么该方法就是有事务管理，不管该方法中有多少条sql语句要执行
     * 那么都是使用同一个事务去操作sql的执行，要么这是sql都成功，要么都失败
     * 当执行某一个sql语句的时候，如果出现了异常，那么该方法上的事务启动回滚的作用，操作数据commit失效
     * 同时异常了，程序终止了，也需要资源释放掉，连接对象释放掉，
     * @Transactional该注解能够作用那些地方
     * 1.可以卸载实现类中方法上面，该方法就事务管理的机制
     * 2.可以在实现类上面,那么该实现类中的所有的方法都有事务管理的机制
     * Transactional注解里面加了
     * 1.readOnly = true  就说该方法中的所有使用Connection去操作数据库表中的数据只能是查询，不能是其他的
     * 2.timeout = -1 相当于Connection一致别占用，下一个程序要去连接数据库，
     * 那么就会堵塞在那么，等资源连接释放掉，才能继续去操作数据库  秒为单位
     * timeout = 3 程序3中还没有执行完，那么就会报错。
     * rollbackFor = 写的是类类型   根据这个类来回滚
     * noRollbackFor = ArithmeticException.class  不回滚方法中的sql操作，会被执行，会改变表中的数据
     * rollbackForClassName= 类的全路径
     * propagation = Propagation.REQUIRED  事务的传递性，默认就是将调用的事务传递到被调用这方法里面
     * 当被调用者出现异常时候，那么就会使用调用的事务惊醒回滚操作。
     * propagation = Propagation.REQUIRES_NEW 在 b 方法里面是该事务的属性时候，它所表达的意思：
     * 新开一个事务，然后执行的对应sql,使用自己的事务去提交数据到表里面
     * 能买几本就几本，知道买不了位置
     */
    @Override
    //@Transactional(propagation = Propagation.REQUIRES_NEW)  //mysql中数据操作方式：默认是可重复读
    public boolean B(int bookId, int userId) {
        /*try {
            //线程修眠3秒
            TimeUnit.SECONDS.sleep(5);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }*/
        //声明boolean的变量
        Boolean falg = false;
        if(bookId <=0 || userId <=0){
            log.info("书的id或者用户Id不能为空：{},{}",bookId,userId);
            return falg;
        }
        // 查询输的价格
        int price = bookMapper.getPriceByBookId(bookId);
        if(price>0){
            //买了一个本书，更新库存
            Integer num = bookMapper.updateStock(bookId,1);
            if(num>0){
                //余额扣除成功了
                Integer suce = bookMapper.updateBalance(userId, price);
                if(suce>0){
                    log.info("书买到，余额也扣除成功了！");
                    falg =true;
                }
            }
        }
        /*
        * 当1除以0在运行时候出现某一个类的异常，
        * 那么就可以在@Transactional(属性里面设置这个类回滚机制)
        * 也就是根据这个异常的类来定义改事务要不要回滚
        * 算术异常： ArithmeticException
        * */
        //System.out.println(1/0);
        return falg;
    }
}
